:- module(dictionary_access, [
        ditem_entry/3,
	ditem_definition/3,
        ditem_synonyms/3,
        ditem_generalization/3,
        ditem_translation/3,
        ditem_check/2,
        ditem_update/3,
        dictionary_entries/3,
%% Types
        dictionary/1,
        ditem/1,
	ditem_or_stream/1,
	trans/1,
        ditem_field/1
        ], [assertions,basicmodes,regtypes]).


:- comment(title, "Creating and Consulting Dictionaries").
:- comment(subtitle, "AMOS Project").


:- comment(author, "Manuel Carro and the CLIP Group").
:- comment(author, "Facultad de Inform@'{a}tica").
:- comment(author, "Universidad Polit@'{e}cnica de Madrid").
:- comment(author, "@tt{clip@@dia.fi.upm.es}").
:- comment(author, "@tt{http://www.clip.dia.fi.upm.es/}").

:- comment(module, "

Dictionaries hold the terms to be used both to describe Open Source
Packages and to state which capabilities are of interest when
performing a search.  Each dictionary consists of a set of terms
(dictionary items); each of these, in turn, may have synonyms (in
order to make programmers and users coming from different knowledge
areas to use the same dictionary) and term descriptions (in order to
clarify what is the exact meaning attributed to every dictionary
term).  The dictionary is assumed to grow steadily until a sufficient
size has been reached, when it should stabilize.


The operations herein included are aimed at creating and consulting
instances of the @em{dictionary} and @em{dictionaryItem} classes.
They may change (for example, to include more operations) should the
ontology evolve in that direction.


@include{three_argument_explanation.lpdoc}

@sp

@include{../Doc/acknowledgements.lpdoc}

@newpage
").

:- include(db('argnamesv/argnamesv')).   % Kludge to use local version
:- use_module(library(sort)).
:- use_module(db(basic_types)).
:- use_module(db(general_check)).



:- argnames is_an_item(
        entry,
	definition,
        synonyms,
        generalization,
        translation).

fields_and_types([
        field(entry,          (nonnull, atom)),
        field(definition,     (null ; atom)),
        field(synonyms,       (null ; list(atom))),
        field(generalization, (null ; list(ditem))),
        field(translation,    (null ; list(trans)))
                 ]).

:- regtype dictionary(?Dict) # "@var{Dict} is a dictionary.".

dictionary(Dict) :- list(Dict).

:- regtype ditem(?Item) 
# "@var{Item} is one entry of the dictionary.".


%jcf%ditem(is_an_item(SearchTerm, Synonyms, Generalization, Translation)):- 
%jcf%        search_term(SearchTerm),
%jcf%        list(Synonyms, search_term),
%jcf%        list(Generalization, search_term),
%jcf%        list(Translation, trans).
ditem(is_an_item${}).

:- regtype ditem_or_stream(?ItemOrStream) 

# "@var{ItemOrStream} is one entry of the dictionary, possibly
  enclosed in a streaming structure (which may be @code{in/1},
  @code{out/1} or @code{stream/1}.".

ditem_or_stream(in(Ditem)):-
	ditem(Ditem).
ditem_or_stream(out(Ditem)):-
	ditem(Ditem).
ditem_or_stream(stream(Ditem)):-
	ditem(Ditem).
ditem_or_stream(Ditem):-
	ditem(Ditem).
        
:- regtype search_term(?Term) 

# "@var{Term} can be searched for.  It is implemented as an atom, in
order to make searches faster, as atom unification is more efficient
than string unification.".

search_term(Term):- atm(Term).

:- regtype ditem_field(?Field)

 # "@var{Field} is a field of a dictionary item.".


ditem_field(Field):-
        member(Field, is_an_item$argnames).


:- pred ditem_entry(Item, Entry, NewItem):
        ditem * string * ditem

#"@var{Entry} is the actual name of the concept stored in
@var{NewItem}. Its presence is required.".

ditem_entry(Item, Entry, NewItem):-
        ditem_update(Item, [entry-Entry], NewItem).

:- pred ditem_definition(Item, Def, NewItem):
        ditem * string * ditem

#"@var{Def} is the actual definition of the concept stored in
@var{NewItem}. Its presence is optional.".

ditem_definition(Item, Def, NewItem):-
        ditem_update(Item, [definition-Def], NewItem).


:- pred ditem_synonyms(Item, Synonyms, NewItem):
        ditem * list(string) * ditem

#"@var{Synonyms} is the list of synonyms for the name of the concept
stored in @var{NewItem}. It may be an empty list.".

ditem_synonyms(Item, Synonyms, NewItem):-
        ditem_update(Item, [synonyms-Synonyms], NewItem).


:- pred ditem_generalization(Item, Generalization, NewItem):
        ditem * list(ditem) * ditem

#"@var{Generalization} is a list of terms which generalize the meaning
of @var{NewItem}.  They are used to broaden the search in order to
obtain (more) matches.  It may be an empty list.".

ditem_generalization(Item, Generalization, NewItem):-
        ditem_update(Item, [generalization-Generalization], NewItem).


:- pred ditem_check(?Ditem, ?WrongFields):
        ditem * list(ditem_field)

# "The already formed dictionary item @var{Ditem} is checked against
the constraints specified in the ontology definition.
@include{check_definition.lpdoc}".


%ditem_check(_Ditem, _WrongFields).
ditem_check(Ditem, WrongFields):-
        fields_and_types(F),
        check_each_field(F, Ditem, WrongFields).

check_each_field([], _Ditem, []).
check_each_field([field(F, T)|Fs], Ditem, W):-
        Ditem = is_an_item${F => V},
        (
            check_field(T, V) ->
            W = RestW
        ;
            W = [F|RestW]
        ),
        check_each_field(Fs, Ditem, RestW).




:- pred ditem_update(+Ditem, +PairList, ?NewDitem):
        ditem * list(pair) * ditem

# "It updates @var{Ditem} to give @var{NewDitem}.  @var{PairList} is a
list of pairs (see @pred{pair}): dash-separated ground terms
@tt{Field-Value}, which are meant to express the name of a field and
its update value.  @tt{Ditem update} is a general call upon which all
the rest of the @tt{update} calls rely.".



ditem_update(Ditem, PairList, NewDitem):-
        NewDitem = is_an_item${}, % New ditem skeleton
        sort(is_an_item$argnames, AllFields),
        keysort(PairList, SortedPairList),  %% All sorted in same order now
        update(AllFields, SortedPairList, Ditem, NewDitem).

update([], _, _, _).
update([Field|Fields], [Field-NewValue|ToUpdate], Ditem, NewDitem):-
        !,
        NewDitem = is_an_item${Field => NewValue},
        update(Fields, ToUpdate, Ditem, NewDitem).
update([Field|Fields], ToUpdate, Ditem, NewDitem):-
        Ditem = is_an_item${Field => Value}, 
        NewDitem = is_an_item${Field => Value}, 
        update(Fields, ToUpdate, Ditem, NewDitem).


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%




:- regtype trans(Langpair) # "@var{LangPair} defines the translation of a
given term into a given language.".

trans(trans(Language, Term)):-
        atm(Language),
        string(Term).


:- pred ditem_translation(Item, Translation, NewItem):
        ditem * list(trans) * ditem

#"@var{Translation} is a list of pairs which define what is the
translation to different languages of the term stored in
@var{NewItem}.".

ditem_translation(Item, Translation, NewItem):-
        ditem_update(Item, [translation-Translation], NewItem).


:- pred dictionary_entries(Dict, Entries, DictAfter):
        dictionary * list(ditem) * dictionary

#"@var{Entries} is the list of items (dictionary entries, as per the
definition above) stored in the dictionary @var{DictAfter}.  There
must be at least a term in the dictionary.".

dictionary_entries(_Dict, _Entries, _DictAfter).


:- comment(version_maintenance,on).

:- comment(version(0*1+5,2003/07/16,17:11*45+'CEST'), "Fixed bug:
   trans/1 of dictionary_access.pl was not exported (general_check.pl
   needed it to check ditem objects).  (Jesus Correas Fernandez)").

:- comment(version(0*1+4,2003/07/16,10:55*15+'CEST'), "Logging
   database operations set for any database insert, update or
   delete. Logging predicate imported from database_files for grouping
   external file operations together.  (Jesus Correas Fernandez)").

:- comment(version(0*1+3,2003/07/16,10:45*59+'CEST'), "Added
   definition field to ditem object.  (Jesus Correas Fernandez)").

:- comment(version(0*1+2,2003/07/04,12:43*29+'CEST'), "Changed
   use_module and include references to use directory aliases.  (Jesus
   Correas Fernandez)").

:- comment(version(0*1+1,2003/02/14,16:26*43+'CET'), "Changed
ItemAfter to NewItem (Manuel Carro)").

:- comment(version(0*1+0,2003/02/13,20:49*07+'CET'), "Initial version,
with all interfaces included.  (Manuel Carro)").
 
